﻿#include"源.h"


//C / C++程序内存分配的⼏个区域：
//1. 栈区（stack）：在执⾏函数时，函数内局部变量的存储单元都可以在栈上创建，函数执⾏结束时
//这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中，效率很⾼，但是分配的内
//存容量有限。 栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等。
//《函数栈帧的创建和销毁》
//2. 堆区（heap）：⼀般由程序员分配释放， 若程序员不释放，程序结束时可能由OS回收 。分配⽅
//式类似于链表。
//3. 数据段（静态区）：（static）存放全局变量、静态数据。程序结束后由系统释放。
//4. 代码段：存放函数体（类成员函数和全局函数）的⼆进制代码。


//C99 中，结构体中的最后⼀个元素允许是未知⼤⼩的数组，这就叫做『柔性数组』成员
//
//柔性数组的特点：
//• 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。
//• sizeof 返回的这种结构⼤⼩不包括柔性数组的内存。
//• 包含柔性数组成员的结构⽤malloc()函数进⾏内存的动态分配，并且分配的内存应该⼤于结构的⼤⼩，以适应柔性数组的预期⼤⼩。

//第⼀个好处是：⽅便内存释放
//如果我们的代码是在⼀个给别⼈⽤的函数中，你在⾥⾯做了⼆次内存分配，并把整个结构体返回给⽤
//⼾。⽤⼾调⽤free可以释放结构体，但是⽤⼾并不知道这个结构体内的成员也需要free，所以你不能
//指望⽤⼾来发现这个事。所以，如果我们把结构体的内存以及其成员要的内存⼀次性分配好了，并返
//回给⽤⼾⼀个结构体指针，⽤⼾做⼀次free就可以把所有的内存也给释放掉。
//第⼆个好处是：这样有利于访问速度.
//连续的内存有益于提⾼访问速度，也有益于减少内存碎⽚。（其实，我个⼈觉得也没多⾼了，反正你
//跑不了要⽤做偏移量的加法来寻址）


//typedef struct st_type
//{
//	int i;
//	int* p_a;
//}type_a;
//int main()
//{
//	type_a* p = (type_a*)malloc(sizeof(type_a));
//	p->i = 100;
//	p->p_a = (int*)malloc(p->i * sizeof(int));
//	//业务处理
//	for (int i = 0; i < 100; i++)
//	{
//		p->p_a[i] = i;
//	}
//	//释放空间
//	free(p->p_a);
//	p->p_a = NULL;
//	free(p);
//	p = NULL;
//	return 0;
//}


//typedef struct st_type
//{
//	int i;
//	int a[0];//柔性数组成员
//}type_a;
//int main()
//{
//	int i = 0;
//	type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
//	//业务处理
//	p->i = 100;
//	for (i = 0; i < 100; i++)
//	{
//		p->a[i] = i;
//	}
//	free(p);
//	p = NULL;
//	return 0;
//}


//typedef struct st_type
//{
//	int i;
//	int a[0];//柔性数组成员
//}type_a;
//int main()
//{
//	printf("%d\n", sizeof(type_a));//输出的是4
//	return 0;
//}


//struct st_type
//{
//	int i;
//	int a[0];//柔性数组成员 - 或者 int a[];
//};


//void Test(void)
//{
//	char* str = (char*)malloc(100);
//	strcpy(str, "hello");
//	free(str);                           //提前释放
//	//修正：str=NULL;
//	if (str != NULL)
//	{
//		strcpy(str, "world");            //非法篡改
//		printf(str);
//	}
//}

//void GetMemory(char** p, int num)
//{
//	 *p = (char*)malloc(num);
//}
//void Test(void)
//{
//	 char* str = NULL;
//	 GetMemory(&str, 100);
//	 strcpy(str, "hello");
//	 printf(str);
//	 free(str);
//	 str = NULL;
//}

//int main()
//{
//	Test();
//	return 0;
//}


//char* GetMemory(void)
//{
//	char p[] = "hello world";
//	return p;
//}
//void Test(void)
//{
//	char* str = NULL;
//	str = GetMemory();//GetMemory 返回后，使用str指针去访问p数组，就是非法访问，因为p数组内存已经还给了操作系统
//	printf(str);      //str是野指针
//}
//int main()
//{
//	Test();
//	return 0;
//}


//void GetMemory(char* p)
//{
//	p = (char*)malloc(100);
//}
////修正：
//// void GetMemory(char** p)
////{
////	*p = (char*)malloc(100);
////}
////或者
////char* GetMemory(char* p)
////{
////	p = (char*)malloc(100);
////  return p;
////}
//void Test(void)                                //1.内存泄露；2.程序崩溃
//{                                              //
//	char* str = NULL;
//	GetMemory(str);// GetMemory(&str)          //str为形参，p仅在函数内部使用，p所指向的空间也仅能在函数内部使用
//	strcpy(str, "hello world");                //对空指针的解引用操作
//	printf(str);
//	//修正：
//	//free(str);
//	//str=NULL;
//}
//int main()
//{
//	Test();
//	return 0;
//}


//忘记释放不再使⽤的动态开辟的空间会造成内存泄漏。
//切记：动态开辟的空间⼀定要释放，并且正确释放。
//尽量谁申请的空间谁释放，如果暂时不能释放，要告诉使用者记得释放
// 
//void test()
//{
//	int* p = (int*)malloc(100);
//	if (NULL != p)
//	{
//		*p = 20;
//	}
//}
//int main()
//{
//	test();
//	while (1);
//	return 0;
//}


//void test()
//{
//	int* p = (int*)malloc(100);
//	free(p);
//	//free(p);//重复释放
//	p = NULL;
//}

//void test()
//{
//	int* p = (int*)malloc(100);
//	if (p == NULL)
//	{
//		return 1;
//	}
//	for (int i = 0; i < 5; i++)
//	{
//		*p = i + 1;
//		p++;
//	}
//	//free(p);//p不再指向动态内存的起始位置
//	p = NULL;
//}

//void test()
//{
//	int a = 10;
//	int* p = &a;
//	//free(p);//free只能释放动态申请的空间
//	p = NULL;
//}

//void test()
//{
//	int i = 0;
//	int* p = (int*)malloc(10 * sizeof(int));
//	if (NULL == p)
//	{
//		exit(EXIT_FAILURE);
//	}
//	for (i = 0; i <= 10; i++)
//	{
//		*(p + i) = i;//当i是10的时候越界访问
//	}
//	free(p);
//}

//int main()
//{
//	test();
//	return 0;
//}


//int main()
//{
//	int* p = (int*)malloc(20);//p有可能是空指针
//	//判断是否为空
//	/*if (p == NULL)
//	{
//		perror("malloc");
//		return 1;
//	}*/
//	assert(p);
//	return 0;
//}


//void* realloc (void* ptr, size_t size);
//realloc - 可以做到对动态开辟内存⼤⼩的调整。
//• ptr 是要调整的内存地址
//• size 调整之后新⼤⼩
//• 返回值为调整之后的内存起始位置。
//• 这个函数调整原内存空间⼤⼩的基础上，还会将原来内存中的数据移动到 新 的空间。
//
// • realloc在调整内存空间的是存在两种情况：
//◦ 情况1: 原有空间之后有⾜够⼤的空间:要扩展内存就直接原有内存之后直接追加空间，原来空间的数据不发⽣变化。
//◦ 情况2: 原有空间之后没有⾜够⼤的空间:原有空间之后没有⾜够多的空间时，扩展的⽅法是：在堆空间上另找⼀个合适⼤⼩
//         的连续空间来使⽤。这样函数返回的是⼀个新的内存地址

//int main()
//{
//	int* p = (int*)malloc(INT_MAX);
//	if (p == NULL)
//	{
//		perror("malloc");
//		return 1;
//	}
//	free(p);
//	p = NULL;
//	return 0;
//}


//realloc可以完成和malloc一样的功能
//int main()
//{
//	realloc(NULL, 20);//等价于 malloc(20);
//	return 0;
//}


//int main()
//{
//	int* p = (int*)malloc(5*sizeof(int));
//	if (p==NULL)
//	{
//		perror("malloc");
//		return 1;
//	}
//	//
//		for (int i = 0; i < 5; i++)
//		{
//			*(p + i) = i + 1;
//		}
//	//将空间调整为40个字节
//	int*ptr=(int*)realloc(p, 40);
//	if (ptr != NULL)
//	{
//		p = ptr;
//		for (int i = 5; i < 10; i++)
//		{
//			*(p + i) = i + 1;
//		}
//		for (int i = 0; i < 10; i++)
//		{
//			printf("%d ", *(p + i));
//		}
//		free(p);
//		p = NULL;
//	}
//	else
//	{
//		perror("realloc");
//		free(p);
//		p = NULL;
//	}
//	return 0;
//}


//void* calloc (size_t num, size_t size);
//calloc - 为 num 个⼤⼩为 size 的元素开辟⼀块空间，并且把空间的每个字节初始化为0。
//与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0

//int main()
//{
//	int* p = (int*)calloc(5, sizeof(int));
//	if (p == NULL)
//	{
//		perror("calloc");
//		return 1;
//	}
//	for (int i = 0; i < 5; i++)
//	{
//		printf("%d ", *(p + i));
//	}
//	return 0;
//}


//void* malloc (size_t size);
//malloc - 数向内存申请⼀块连续可⽤的空间，并返回指向这块空间的指针。
//• 如果开辟成功，则返回⼀个指向开辟好空间的指针。
//• 如果开辟失败，则返回⼀个 NULL 指针，因此malloc的返回值⼀定要做检查。
//• 返回值的类型是 void* ，所以malloc函数并不知道开辟空间的类型，具体在使⽤的时候使⽤者⾃⼰来决定。
//• 如果参数 size 为0，malloc的⾏为是标准是未定义的，取决于编译器

//void free (void* ptr);
//free - ⽤来释放动态开辟的内存。
//• 如果参数 ptr 指向的空间不是动态开辟的，那free函数的⾏为是未定义的。
//• 如果参数 ptr 是NULL指针，则函数什么事都不做。

//malloc和free都声明在 stdlib.h 头⽂件中。

//int main()
//{
//	int num = 0;
//	scanf("%d", &num);
//	int arr[] = { 0 };
//	int* ptr = NULL;
//	ptr = (int*)malloc(num * sizeof(int));
//	if (NULL==ptr)//判断ptr指针是否为空
//	{
//		perror("malloc");
//		return 1;
//	}
//	for (int i = 0; i < num; i++)
//	{
//		*(ptr + i) = i+1;
//	}
//	free(ptr);//释放ptr所指向的动态内存 - 传递给free的是想释放空间的起始地址
//	ptr = NULL;
//	return 0;
//}